import React, {
  useRef,
  useState,
  useImperativeHandle,
  forwardRef,
} from 'react'
import classNames from 'classnames'
import { convertPx } from '@/library/tool-kits/convert-px'
import { useThrottleFn } from '@/library/hooks/use-throttle-fn'
import { warn } from '@/library/tool-kits/log'
import { withNativeProps } from '../../utils/native-props'
import { traverseReactNode } from '../../utils/traverse-react-node'
import { Panel } from './panel'
import { Sidebar } from './sidebar'
import type {
  IndexBarProps,
  IndexBarRef,
  SidebarProps,
} from './type'

const classPrefix = 'rt-index-bar'

export const IndexBar = forwardRef<IndexBarRef, IndexBarProps>((props, ref) => {
  const {
    children,
    onIndexChange,
    sticky = true,
  } = props

  const titleHeight = convertPx(35)

  const bodyRef = useRef<HTMLDivElement>(null)

  const indexItems: SidebarProps['indexItems'] = []

  const panels: React.ReactElement[] = []

  traverseReactNode(children, (child) => {
    if (!React.isValidElement(child)) return
    if (child.type !== Panel) {
      warn(
        'IndexBar',
        'The children of `IndexBar` must be `IndexBar.Panel` components.',
      )
      return
    }
    indexItems.push({
      index: child.props.index,
      brief: child.props.brief ?? child.props.index.charAt(0),
    })
    panels.push(
      withNativeProps(
        child.props,
        <div
          key={child.props.index}
          data-index={child.props.index}
          className={`${classPrefix}-anchor`}
        >
          <div className={`${classPrefix}-anchor-title`}>
            {child.props.title || child.props.index}
          </div>
          {child.props.children}
        </div>,
      ),
    )
  })

  const [activeIndex, setActiveIndex] = useState(() => {
    const firstItem = indexItems[0]
    return firstItem ? firstItem.index : null
  })

  const scrollTo = (index: string) => {
    const body = bodyRef.current
    if (!body) return

    const elements = body.children
    for (let i = 0; i < elements.length; i++) {
      const panel = elements.item(i) as HTMLElement
      if (!panel) continue
      const panelIndex = panel.dataset.index
      if (panelIndex === index) {
        // 当你尝试设置scrollTop超出其最大值时，浏览器会自动将其设置为最大可能值，即元素的最底部与视窗的交集所对应的滚动距离
        body.scrollTop = panel.offsetTop
        setActiveIndex(index)
        activeIndex !== index && props.onIndexChange?.(index)
        return
      }
    }
  }

  const { run: checkActiveIndex } = useThrottleFn(
    () => {
      const body = bodyRef.current
      if (!body) return

      const { scrollTop } = body
      const elements = body.getElementsByClassName(`${classPrefix}-anchor`)
      for (let i = 0; i < elements.length; i++) {
        const panel = elements.item(i) as HTMLElement
        if (!panel) continue
        const panelIndex = panel.dataset.index
        if (!panelIndex) continue
        // offsetTop 是定值
        if (panel.offsetTop + panel.clientHeight - titleHeight > scrollTop) {
          setActiveIndex(panelIndex)
          activeIndex !== panelIndex && onIndexChange?.(panelIndex)
          return
        }
      }
    },
    { wait: 50, trailing: true, leading: true },
  )

  useImperativeHandle(ref, () => ({ scrollTo }))

  return withNativeProps(
    props,
    <div
      className={classNames(`${classPrefix}`, {
        [`${classPrefix}-sticky`]: sticky,
      })}
    >
      <Sidebar
        indexItems={indexItems}
        activeIndex={activeIndex}
        onActive={(index) => {
          scrollTo(index)
        }}
      />
      <div
        className={`${classPrefix}-body`}
        ref={bodyRef}
        onScroll={checkActiveIndex}
      >
        {panels}
      </div>
    </div>,
  )
})
